/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugin_render.h"

#include <multimedia/image_framework/image/image_source_native.h>
#include <multimedia/image_framework/image_pixel_map_mdk.h>
#include <cstdint>
#include <string>
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <log.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <uv.h>

#include "napi/n_func_arg.h"

std::unordered_map<std::string, PluginRender *> PluginRender::m_instance;
OH_NativeXComponent_Callback PluginRender::m_callback;
std::unordered_map<std::string, CallbackContext *> PluginRender::m_jsCallback;

void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceCreatedCB");
    if ((nullptr == component) || (nullptr == window)) {
        LOGE("OnSurfaceCreatedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {
        LOGE("OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }
    
    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    uint64_t width;
    uint64_t height;
    int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    if ((OH_NATIVEXCOMPONENT_RESULT_SUCCESS == xSize) && (nullptr != render)) {
        render->m_window = window;
        render->m_width = width;
        render->m_height= height;
        render->player_ = std::make_unique<Player>();
    }
}

void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceChangedCB");
    if ((nullptr == component) || (nullptr == window)) {
        LOGE("OnSurfaceChangedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {
        LOGE("OnSurfaceChangedCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    if (nullptr != render) {
        LOGI("surface changed");
    }
}

void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
{
    LOGI("OnSurfaceDestroyedCB");
    if ((nullptr == component) || (nullptr == window)) {
        LOGE("OnSurfaceDestroyedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {
        LOGE("OnSurfaceDestroyedCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    PluginRender::Release(id);
}

void DispatchTouchEventCB(OH_NativeXComponent *component, void *window)
{
    LOGI("DispatchTouchEventCB");
    if ((nullptr == component) || (nullptr == window)) {
        LOGE("DispatchTouchEventCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {
        LOGE("DispatchTouchEventCB: Unable to get XComponent id");
        return;
    }
    
    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    if (nullptr != render) {
        LOGI("surface touch");
        OH_NativeXComponent_TouchEvent touchEvent;
        int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
        if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
            LOGE("Touch Info : x = %{public}f, y = %{public}f screenx = %{public}f, screeny = %{public}f",
                 touchEvent.x, touchEvent.y, touchEvent.screenX, touchEvent.screenY);
            for (uint32_t i = 0; i < touchEvent.numPoints; i++) {
                LOGE("Touch Info : dots[%{public}d] id %{public}d x = %{public}f, y = %{public}f", i,
                     touchEvent.touchPoints[i].id, touchEvent.touchPoints[i].x, touchEvent.touchPoints[i].y);
                LOGE("Touch Info : screenx = %{public}f, screeny = %{public}f",
                     touchEvent.touchPoints[i].screenX, touchEvent.touchPoints[i].screenY);
                OH_NativeXComponent_TouchPointToolType toolType;
                float tiltX = 0.0;
                float tiltY = 0.0;
                int32_t ret1;
                int32_t ret2;
                int32_t ret3;
                ret1 = OH_NativeXComponent_GetTouchPointToolType(component, i, &toolType);
                ret2 = OH_NativeXComponent_GetTouchPointTiltX(component, i, &tiltX);
                ret3 = OH_NativeXComponent_GetTouchPointTiltY(component, i, &tiltY);
                LOGE("Touch Info : [%{public}d] %{public}u, %{public}f, %{public}f",
                     i, toolType, tiltX, tiltY);
            }
            render->player_->XComponentClick(touchEvent.x, touchEvent.y);
        } else {
            LOGE("Touch fail");
        }
    }
}

PluginRender::PluginRender(std::string &id)
{
    this->m_id = id;
    OH_NativeXComponent_Callback *renderCallback = &PluginRender::m_callback;
    renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB;
    renderCallback->OnSurfaceChanged = OnSurfaceChangedCB;
    renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB;
    renderCallback->DispatchTouchEvent = DispatchTouchEventCB;
}

PluginRender *PluginRender::GetInstance(std::string &id)
{
    if (m_instance.find(id) == m_instance.end()) {
        PluginRender *instance = new PluginRender(id);
        m_instance[id] = instance;
        return instance;
    } else {
        return m_instance[id];
    }
}

std::string PluginRender::GetXComponentId(napi_env env, napi_callback_info info)
{
    napi_value thisArg;
    napi_value exportInstance;
    OH_NativeXComponent *nativeXComponent = nullptr;
    std::string id;

    napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL);
    if (napi_ok != napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)) {
        LOGE("Play: napi_get_named_property fail");
        return id;
    }
    
    if (napi_ok != napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent))) {
        LOGE("Play: napi_unwrap fail");
        return id;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize)) {
        LOGE("Play: Unable to get XComponent id");
        return id;
    }
    id = idStr;
    return id;
}

napi_value tesFunc(napi_env env, napi_callback_info info);

napi_value PluginRender::Pause(napi_env env, napi_callback_info info)
{
    LOGI("enter pause");
    std::string idStr = GetXComponentId(env, info);
    if (idStr.empty()) {
        LOGE("GetXComponentId error");
        return nullptr;
    }
    std::string id(idStr);
    LOGI("Pause render id:%{public}s", id.c_str());
    PluginRender *render = PluginRender::GetInstance(id);
    if (render) {
        render->player_->PauseAndResume();
    }
    return nullptr;
    LOGI("end pause");
}

napi_value PluginRender::Stop(napi_env env, napi_callback_info info)
{
    LOGI("enter Stop");
    std::string idStr = GetXComponentId(env, info);
    if (idStr.empty()) {
        LOGE("GetXComponentId error");
        return nullptr;
    }
    std::string id(idStr);
    PluginRender *render = PluginRender::GetInstance(id);
    if (render && render->player_) {
        render->player_->Stop();
    }
    LOGI("end Stop");
    return nullptr;
}

napi_value PluginRender::SetLoop(napi_env env, napi_callback_info info)
{
    LOGI("enter SetLoop");
    std::string idStr = GetXComponentId(env, info);
    if (idStr.empty()) {
        LOGE("GetXComponentId error");
        return nullptr;
    }
    std::string id(idStr);
    LOGI("SetLoop render id:%{public}s", id.c_str());
    PluginRender *render = PluginRender::GetInstance(id);
    if (render) {
        NFuncArg funcArg(env, info);
        if (!funcArg.InitArgs(NARG_CNT::ONE)) {
            return nullptr;
        }
        napi_value v1 = funcArg.GetArg(NARG_POS::FIRST);
        NVal nVal(env, v1);
        auto [succ, resData] = nVal.ToInt32();
        LOGI("SetLoop loop: %{public}d", resData);
        if (succ) {
            render->player_->SetLoop(resData);
        } else {
            LOGE("SetLoop cont get val");
        }
    }
    LOGI("end SetLoop");
    return nullptr;
}

void PluginRender::Export(napi_env env, napi_value exports)
{
    if ((nullptr == env) || (nullptr == exports)) {
        LOGE("Export: env or exports is null");
        return;
    }
    this->env_ = env;

    napi_property_descriptor desc[] = {
        { "play", nullptr, PluginRender::Play, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "on", nullptr, PluginRender::On, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "pause", nullptr, PluginRender::Pause, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "stop", nullptr, PluginRender::Stop, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setLoop", nullptr, PluginRender::SetLoop, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "tes", nullptr, tesFunc, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) {
        LOGE("Export: napi_define_properties failed");
    }
}

void ParseMixParam(std::vector<MixInputData> &iptData, napi_env env, NFuncArg &funcArg)
{
    napi_value v2 = funcArg.GetArg(NARG_POS::SECOND);
    if (!v2) {
        LOGE("not get second param");
    }
    
    NVal nVal2(env, v2);
    auto [succ1, isArray] = nVal2.IsArray();
    if (!succ1 || !isArray) {
        LOGE("the second param is not array");
    }
    uint32_t len = 0;
    napi_get_array_length(env, v2, &len);
    iptData.resize(len);
    if (len == 0) {
        LOGE("the array is empty");
        return;
    }
        
    for (uint32_t i = 0; i < len; i++) {
        napi_value element;
        napi_get_element(env, v2, i, &element);
//                 args[i] = element;
        NVal nValOneOpt(env, element);
        std::string propName = "txt";
        if (nValOneOpt.HasProp(propName)) {
            auto [succ, resData, length] = nValOneOpt.GetProp(propName).ToUTF8String();
            if (succ) {
                LOGI("prop data:%{public}s, len :%{public}lu", resData.get(), length);
                iptData[i].txt = resData.get();
            }
        }
        std::string propImage = "imgUri";
        if (nValOneOpt.HasProp(propImage)) {
            auto [succ, resData, length] = nValOneOpt.GetProp(propImage).ToUTF8String();
            if (succ) {
                LOGI("prop data:%{public}s, len :%{public}lu", resData.get(), length);
                iptData[i].imgUri = resData.get();
            }
        }
    }
}

void CreateAnimConifg(napi_env env, JSAnimConfig jsAnimConfig, NVal &animConfig)
{
    auto alphaRect = NVal::CreateObject(env);
    auto x = NVal::CreateInt64(env, jsAnimConfig.alphaPointRect.x);
    auto y = NVal::CreateInt64(env, jsAnimConfig.alphaPointRect.y);
    auto w = NVal::CreateInt64(env, jsAnimConfig.alphaPointRect.w);
    auto h = NVal::CreateInt64(env, jsAnimConfig.alphaPointRect.h);
    alphaRect.AddProp("x", x.val_);
    alphaRect.AddProp("y", y.val_);
    alphaRect.AddProp("w", w.val_);
    alphaRect.AddProp("h", h.val_);
    
    auto rgbRect = NVal::CreateObject(env);
    auto x1 = NVal::CreateInt64(env, jsAnimConfig.rgbPointRect.x);
    auto y1 = NVal::CreateInt64(env, jsAnimConfig.rgbPointRect.y);
    auto w1 = NVal::CreateInt64(env, jsAnimConfig.rgbPointRect.w);
    auto h1 = NVal::CreateInt64(env, jsAnimConfig.rgbPointRect.h);
    rgbRect.AddProp("x", x1.val_);
    rgbRect.AddProp("y", y1.val_);
    rgbRect.AddProp("w", w1.val_);
    rgbRect.AddProp("h", h1.val_);
    
    auto version = NVal::CreateInt64(env, jsAnimConfig.version);
    auto totalFrames = NVal::CreateInt64(env, jsAnimConfig.totalFrames);
    auto width = NVal::CreateInt64(env, jsAnimConfig.width);
    auto height = NVal::CreateInt64(env, jsAnimConfig.height);
    auto videoWidth = NVal::CreateInt64(env, jsAnimConfig.videoWidth);
    auto videoHeight = NVal::CreateInt64(env, jsAnimConfig.videoHeight);
    auto orien = NVal::CreateInt64(env, jsAnimConfig.orien);
    auto fps = NVal::CreateInt64(env, jsAnimConfig.fps);
    auto isMix = NVal::CreateBool(env, jsAnimConfig.isMix);
    auto currentFrame = NVal::CreateInt64(env, jsAnimConfig.currentFrame);
    
    animConfig.AddProp("version", version.val_);
    animConfig.AddProp("totalFrames", totalFrames.val_);
    animConfig.AddProp("width", width.val_);
    animConfig.AddProp("height", height.val_);
    animConfig.AddProp("videoWidth", videoWidth.val_);
    animConfig.AddProp("videoHeight", videoHeight.val_);
    animConfig.AddProp("orien", orien.val_);
    animConfig.AddProp("fps", fps.val_);
    animConfig.AddProp("isMix", isMix.val_);
    animConfig.AddProp("alphaPointRect", alphaRect.val_);
    animConfig.AddProp("rgbPointRect", rgbRect.val_);
    animConfig.AddProp("currentFrame", currentFrame.val_);
}

void Callback(void *asyncContext)
{
    LOGI("enter Callback");
    if (asyncContext == nullptr) {
        LOGE("Callback asyncContext is nullptr");
        return;
    }
    uv_loop_s *loop = nullptr;
    CallbackContext *context = (CallbackContext *)asyncContext;
    napi_status status = napi_get_uv_event_loop(context->env, &loop);
    if (status != napi_ok) {
        LOGE("Callback con not napi_get_uv_event_loop %{public}d", status);
        return;
    }
    uv_work_t *work = new uv_work_t;
    work->data = context;
    uv_queue_work(
        loop, work, [](uv_work_t *work) { LOGI("enter uv_work_t Callback"); },
        [](uv_work_t *work, int status)
        {
            LOGI("enter complete Callback");
            CallbackContext *context = (CallbackContext *)work->data;
            napi_handle_scope scope = nullptr;
            napi_open_handle_scope(context->env, &scope);
            napi_value callback = nullptr;
            napi_get_reference_value(context->env, context->callbackRef, &callback);
            if (context && context->vapState == VapState::UNKNOWN) {
                napi_call_function(context->env, nullptr, callback, 0, nullptr, nullptr);
            } else if (context && context->vapState == VapState::FAILED) {
                napi_value ret[2];
                napi_status st = napi_create_int32(context->env, context->vapState, &ret[0]);
                napi_create_int32(context->env, context->err, &ret[1]);
                napi_value res = nullptr;
                napi_call_function(context->env, nullptr, callback, 2, ret, &res);
            } else if (context && (context->vapState == VapState::RENDER || context->vapState == VapState::START)) {
                auto animConfig = NVal::CreateObject(context->env);
                CreateAnimConifg(context->env, context->jsAnimConfig, animConfig);
                napi_value ret[2];
                napi_status st = napi_create_int32(context->env, context->vapState, &ret[0]);
                ret[1] = animConfig.val_;
                napi_value res = nullptr;
                napi_call_function(context->env, nullptr, callback, 2, ret, &res);
            } else if (context) {
                napi_value rev = nullptr;
                napi_status st = napi_create_int32(context->env, context->vapState, &rev);
                napi_value res = nullptr;
                napi_call_function(context->env, nullptr, callback, 1, &rev, &res);
            }
            napi_close_handle_scope(context->env, scope);
//             delete context;
            delete work;
        }
    );
}

void PluginRender::ParseCallback(void *&playDoneCallbackData, napi_env env,
    napi_value val, std::string id, std::string type)
{
    LOGI("enter ParseCallback");
    if (!val) {
        LOGE("not get callback param");
        return;
    }
    
    CallbackContext *asyncContext;
    if (refMap_.find(type) != refMap_.end()) {
        napi_ref ref = refMap_.at(type);
        napi_delete_reference(env, ref);
        refMap_.erase(type);
    }

    if (m_jsCallback.find(id) == m_jsCallback.end()) {
        asyncContext = new CallbackContext();
        m_jsCallback[id] = asyncContext;
    } else {
        asyncContext = m_jsCallback[id];
    }
    asyncContext->env = env;
    
    napi_status createRet = napi_create_reference(env, val, 1, &asyncContext->callbackRef);
    if (createRet != napi_ok) {
        LOGE("napi_create_reference error-%{public}d", createRet);
        return;
    }

    playDoneCallbackData = asyncContext;
}

napi_value PluginRender::Play(napi_env env, napi_callback_info info)
{
    LOGI("enter Play");
    NFuncArg funcArg(env, info);
    if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::FOUR)) {
        return nullptr;
    }
    napi_value v1 = funcArg.GetArg(NARG_POS::FIRST);
    NVal nVal(env, v1);
    auto [succ, resData, length] = nVal.ToUTF8String();
    std::string str = resData.get();
    LOGW("Get Count of argc %{public}zu str:%{public}s", funcArg.GetMaxArgc(), str.c_str());
    std::string idStr = GetXComponentId(env, info);
    if (idStr.empty()) {
        return nullptr;
    }
    std::string id(idStr);
    PluginRender *render = PluginRender::GetInstance(id);
    if (render) {
        std::string uri = str; // "/storage/Users/currentUser/Documents/1.mp4";
        if (uri.empty()) {
            LOGE("Play: uri empty");
            return nullptr;
        }
        SampleInfo sampleInfo;
        std::vector<MixInputData> iptData;
        if (funcArg.GetMaxArgc() >= 2) {
            ParseMixParam(iptData, env, funcArg);
        }
        if (funcArg.GetMaxArgc() >= 3) {
            std::string type = "playDone";
            napi_value v3 = funcArg.GetArg(NARG_POS::THIRD);
            sampleInfo.PlayDoneCallback = &Callback;
            render->ParseCallback(sampleInfo.playDoneCallbackData, env, v3, render->m_id + type, type);
            render->env_ = env;
            LOGI("Play ParseCallback :%{public}p :%{public}p", sampleInfo.playDoneCallbackData, v3);
            if (sampleInfo.playDoneCallbackData) {
                CallbackContext *context = (CallbackContext *)sampleInfo.playDoneCallbackData;
                render->PutRef(type, context->callbackRef);
            }
        }
        int32_t speed = 1;
        if (funcArg.GetMaxArgc() >= 4) {
            napi_value v4 = funcArg.GetArg(NARG_POS::FOURTH);
            NVal nVal4(env, v4);
            auto [succ, speed_] = nVal4.ToInt32();
            if (speed_ > 1) {
                speed = speed_;
            }
        }
        sampleInfo.uri = uri;
        sampleInfo.window = static_cast<NativeWindow*>(render->m_window);
        sampleInfo.width = render->m_width;
        sampleInfo.height = render->m_height;
        sampleInfo.iptData = iptData;
        render->player_->Init(sampleInfo, speed);
        LOGI("Player Start executed");
    }
    return nullptr;
}

napi_value tesFunc(napi_env env, napi_callback_info info)
{
    LOGI("enter tesFunc");
    NFuncArg funcArg(env, info);
    if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
        return nullptr;
    }
    napi_value v1 = funcArg.GetArg(NARG_POS::FIRST);
    NVal nVal(env, v1);
    auto [succ, resData, length] = nVal.ToUTF8String();
    napi_value v2 = funcArg.GetArg(NARG_POS::SECOND);
    if (!v2) {
        LOGE("not get second param");
    }
    NVal nVal2(env, v2);
    auto [succ1, isArray] = nVal2.IsArray();
    if (succ1 && isArray) {
        uint32_t len = 0;
        napi_get_array_length(env, v2, &len);
        const static uint32_t LENGTH = len;
        std::vector<napi_value> args(LENGTH);
        for (uint32_t i = 0; i < length; i++) {
            napi_value element;
            napi_get_element(env, v2, i, &element);
            args[i] = element;
        }
        napi_value oneOpt = args[0];
        std::string propName = "txt";
        NVal nValOneOpt(env, oneOpt);
        if (nValOneOpt.HasProp(propName)) {
            auto [succ, resData, length] = nValOneOpt.GetProp(propName).ToUTF8String();
            if (succ) {
                LOGI("prop data:%{public}s, len :%{public}lu", resData.get(), length);
            }
        }
    } else {
        LOGE("the second param is not array");
    }
    LOGI("end tesFunc");
    return nullptr;
}

napi_value PluginRender::On(napi_env env, napi_callback_info info)
{
    NFuncArg funcArg(env, info);
    if (!funcArg.InitArgs(NARG_CNT::TWO)) {
        LOGE("func 'on' Init args error.");
        return nullptr;
    }
    
    napi_value thisArg = funcArg.GetThisVar();
    std::string idStr = GetXComponentId(env, info);
    if (idStr.empty()) {
        LOGE("GetXComponentId error");
        return nullptr;
    }
    std::string id(idStr);
    PluginRender *render = PluginRender::GetInstance(id);
    if (render) {
        if (render->player_->IsRunning()) {
            LOGE("the player is running...plz call this func before play.");
            return nullptr;
        }
        napi_value v1 = funcArg.GetArg(NARG_POS::FIRST);
        NVal nVal(env, v1);
        auto [succ, resData, length] = nVal.ToUTF8String();
        std::string str = resData.get();
        if (succ) {
            LOGI("res data:%{public}s, len :%{public}lu", resData.get(), length);
        }
        LOGW("Get Count of argc %{public}zu str:%{public}s", funcArg.GetMaxArgc(), str.c_str());
        
        if (str != "stateChange" && str != "click") {
            LOGE("not support event");
            return nullptr;
        }
        void (*callback)(void *context) = nullptr;
        void *callbackData = nullptr;
        if (funcArg.GetMaxArgc() >= 2) {
            napi_value v2 = funcArg.GetArg(NARG_POS::SECOND);
            callback = &Callback;
            render->ParseCallback(callbackData, env, v2, render->m_id + str, str);
            render->env_ = env;
            CallbackContext *context = (CallbackContext *)callbackData;
            render->PutRef(str, context->callbackRef);
        }
        if (callback && callbackData) {
            CallbackContext *context = (CallbackContext *)callbackData;
            if (str == "click") {
                render->player_->SetClickCallBack(callback, callbackData);
            } else if (str == "stateChange") {
                render->player_->SetCallBack(callback, callbackData);
            }
        }
    }
    return nullptr;
}

void PluginRender::Release(std::string &id)
{
    PluginRender *render = PluginRender::GetInstance(id);
    if (nullptr != render) {
        if (!render->refMap_.empty()) {
            for (auto it = render->refMap_.begin(); it != render->refMap_.end();) {
                napi_delete_reference(render->env_, it->second);
                it = render->refMap_.erase(it);
            }
        }
        for (auto type : render->supportType_) {
            std::string key = id + type;
            if (m_jsCallback.find(key) != m_jsCallback.end()) {
                delete m_jsCallback.at(key);
                m_jsCallback.erase(m_jsCallback.find(key));
            }
        }
//         delete render;
//         render = nullptr;
        m_instance.erase(m_instance.find(id));
    }
}